package net.e_agri.wap;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.arnx.jsonic.JSON;
import net.e_agri.action.Authentication;
import net.e_agri.cache.AgriCacheManager;
import net.e_agri.cache.ContentCacheManager;
import net.e_agri.json.FriendsCommunitiesConverter;
import net.e_agri.json.JSonConverter;
import net.e_agri.json.TopicConverter;
import net.e_agri.wap.section.TopPageSection;
import net.htmlparser.jericho.Source;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

@SuppressWarnings("serial")
public class WapServlet extends HttpServlet {
	
	private static String uriRoot = "http://test1.e-agri.net/";

	private static final Log log = LogFactory.getLog(WapServlet.class);

	private static ContentCacheManager cacheManager = null;

	public void init(){
		uriRoot = getServletContext().getInitParameter("UriRoot");
		cacheManager = new AgriCacheManager();
	}
	
	private Authentication doLogin(String id, String pwd) throws IOException{
		Authentication auth = new Authentication();
		auth.setRootUri(uriRoot)
				.setUid(id)
				.setPwd(pwd)
				.logIn();
		log.info("sessid for " + id + ":" + auth.getSessid());

		return auth;
	}
	
	private String parseTopPageContent(String content) throws IOException{
		//Posts
		StringBuffer buf = new StringBuffer();
		buf.append("{");

		String sessId = "";
		String pattern = "&amp;sessid=([^\"]*)\\\"";
		Matcher mat = Pattern.compile(pattern).matcher(content);
		if(mat.find()){
			sessId = mat.group(1);
			buf.append("\"sessid\":\"").append(sessId).append("\"");
		}else
			log.info("sessid not found.:" + pattern);
		
		Map<String,String> sectionHeaders = new LinkedHashMap<String,String>();
		sectionHeaders.put("0", "マイフレンド最新日記");
		sectionHeaders.put("1", "コミュニティ最新書き込み");
		sectionHeaders.put("2", "マイフレンド最新アルバム");
		sectionHeaders.put("3", "マイフレンド以外の最新日記");
		sectionHeaders.put("4", "未参加コミュニティ最新書き込み");
		buf.append(",\"sectionHeaders\":").append(JSON.encode(sectionHeaders));

		final String STN = "\\s\\t\\n";
		buf.append(",\"posts\":");
		pattern = "<li>["+STN+"]*<span class=\\\"date\\\">["+STN+"]*([^<"+STN+"]*)["+STN+"]*<\\/span>["+STN+"]*"
						+ "<a href=\\\"(\\.\\/[^\\\">]*)\\\">([^<>]*)<\\/a>["+STN+"]*([^<>"+STN+"]*)["+STN+"]*<";	
		mat = Pattern.compile(pattern).matcher(content);
		buf.append(JSonConverter.convertMatcher(mat, Arrays.asList("date","url","title","auther"), new TopPageSection()));
		buf.append("}");

		//Cache all linked topics from top
		List<String> topicUrls = new ArrayList<String>();
		mat = mat.reset();

		while(mat.find())
			if(!cacheManager.isValid(uriRoot + mat.group(2)))
				topicUrls.add(uriRoot + mat.group(2));

		log.debug("topicUrls = " + topicUrls.toString());
		if(topicUrls.size() > 0)
			cacheManager.scheduleUpdateAll(topicUrls);

		return buf.toString();
	}

	@SuppressWarnings("unchecked")
	public void doPost(HttpServletRequest req, HttpServletResponse resp)
		throws IOException {

		final int FIELD_URL = 1;
		StringBuffer buf = new StringBuffer();

		if(req.getParameter("login") != null){

			Enumeration<String> e = (Enumeration<String>)req.getHeaderNames();
			String headerName = null;
			while(e.hasMoreElements()){
				headerName = e.nextElement();
				buf.append("\n\t").append(headerName).append("\t=[")
					.append(req.getHeader(headerName)).append("]");
			}
			log.info(buf.toString());

			Authentication auth = doLogin(req.getParameter("login"), req.getParameter("pwd"));
			if(auth.getSessid()!=null){
				req.getSession(true).setAttribute("auth", auth);
				resp.getWriter().write(auth.getSessid());
				resp.getWriter().flush();
				resp.setStatus(200);
			}else{
				resp.getWriter().write("FAIL");
				resp.setStatus(500);
			}
		}

		if(req.getParameter("logout") != null)
			((Authentication)req.getSession().getAttribute("auth")).logOut();

		if(req.getParameter("a") != null){

			resp.setContentType("text/html; charset=utf-8");
			resp.setHeader("Content-Encoding", "gzip");
			//resp("text/javascript+json; charset=utf-8");

			Authentication auth = (Authentication)req.getSession().getAttribute("auth");
			if(auth == null){
				resp.getWriter().write("NOT AUTHORIZED");
				resp.setStatus(403);
			}else if(auth != null && auth.getSessid() == null){
				resp.getWriter().write("NOT AUTHORIZED");
				resp.setStatus(403);
			}else{
				if("top".equals(req.getParameter("a"))){

					Map<String, String> urlsMap = new LinkedHashMap<String, String>();
					urlsMap.put(uriRoot, "top");
					urlsMap.put(uriRoot + "?m=pc&a=page_fh_diary_list", "diary");
					urlsMap.put(uriRoot + "?m=pc&a=page_fh_friend_list", "friends");
					urlsMap.put(uriRoot + "?m=pc&a=page_fh_com_list", "communities");
					urlsMap.put(uriRoot + "?m=pc&a=page_fh_album_list", "album");
					urlsMap.put(uriRoot + "?m=pc&a=page_fh_event_list", "events");
					//urlsMap.put(uriRoot + "?m=pc&a=page_h_message_box&box=inbox", "inbox");
					//urlsMap.put(uriRoot + "?m=pc&a=page_h_message_box&box=outbox", "outbox");

					List<String> urls = new ArrayList<String>();
					urls.addAll(urlsMap.keySet());
					Map<String, String> topContentMap = HttpMethod.doGetAsync(urls);
					String content = null;
					String cacheUrl = null;

					GZIPOutputStream gzip = new GZIPOutputStream(resp.getOutputStream());
					gzip.write("{".getBytes());

					urls.clear();
					for(String url : urlsMap.keySet()){
						
						if(url.equals(uriRoot)){
							content = parseTopPageContent(topContentMap.get(url));
						}else{
							if(url.indexOf("page_fh_diary_list") != -1
							|| url.indexOf("a=page_h_message_box") != -1){
								
								content = new TopicConverter().parseSource(new Source(topContentMap.get(url)), url);

							}else{
								content = new FriendsCommunitiesConverter().parseSource(new Source(topContentMap.get(url)), url);
							}
							
							//Pre-Cache list
							for(List<String> row : (List<List<String>>)((Map<String, Object>)JSON.decode(content, Map.class)).get("data")){
								cacheUrl = uriRoot + "./?m=pc&" + row.get(FIELD_URL)
														.replace("a=page_f_home", "a=page_fh_diary_list")
														.replace("a=page_c_home", "a=page_c_topic_list");
								if(!cacheManager.isValid(cacheUrl))
									urls.add(cacheUrl);
							}
						}

						if(!url.equals(uriRoot))
							gzip.write(",".getBytes());

						gzip.write(("\"" + urlsMap.get(url) + "\":").getBytes());
						gzip.write(content.getBytes("utf-8"));
					}
					gzip.write("}".getBytes());
					gzip.flush();
					gzip.close();

					cacheManager.scheduleUpdateAll(urls);
				}else{
						String qryString = req.getQueryString().trim();
						if(qryString.startsWith("./?"))
							qryString = uriRoot + qryString;
						else
							qryString = uriRoot + "./?m=pc&" + qryString;
						
						if("page_f_home".equals(req.getParameter("a"))){
							qryString = qryString.replace("a=page_f_home", "a=page_fh_diary_list");
						}else if("page_c_home".equals(req.getParameter("a"))){
							qryString = qryString.replace("a=page_c_home", "a=page_c_topic_list");
						}
						
						//a=page_h_message&target_c_message_id=12&box=outbox
						if("page_h_message".equals(req.getParameter("a"))){
							if(qryString.indexOf("&box=") != -1)
								qryString = qryString.substring(0, qryString.indexOf("&box="));
						}
	
						log.debug("\n\tqry : [" + req.getQueryString().trim() + "]");
						log.info("\n\tqryString : [" + qryString + "]");

						byte[] content = cacheManager.returnFromCache(qryString, AgriCacheManager.assignConverter(qryString));

						if(qryString.indexOf("a=page_fh_diary_list&") != -1
						|| qryString.indexOf("a=page_c_topic_list&") != -1) {

							List<String> urls = new ArrayList<String>();

							for(List<String> row : (List<List<String>>)((Map<String, Object>)JSON.decode(decodeGZip(content), Map.class)).get("data"))
								urls.add(uriRoot + "./?m=pc&" + row.get(FIELD_URL));

							if(urls.size() > 0 && !cacheManager.isValid(urls.get(0)))
								cacheManager.scheduleUpdateAll(urls);
						}

						resp.getOutputStream().write(content);
						resp.getOutputStream().flush();
						resp.getOutputStream().close();

						//log.info(decodeGZip(content));
				}
			}
		}
	}
	
	private String decodeGZip(byte[] content) throws IOException{

		byte[] bbuf = new byte[4096];
		int bsize = 0;

		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		GZIPInputStream zin = new GZIPInputStream(new ByteArrayInputStream(content));
		
		while((bsize = zin.read(bbuf)) > 0)
			bout.write(bbuf, 0, bsize);
		
		return bout.toString();
	}

	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {

		doPost(req, resp);
	}
}
